﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HdRSpielbox.Mühle
{
    public class AI
    {
        Züge[] Mögliche_Züge;
        public Linie3[] Linien;
        public PointM[] Points;

        public Muehle m;
        public int active_player;

        public AI(Muehle m)
        {
            Mögliche_Züge = new Züge[1];
            this.m = m;
            active_player = Player_to_int(m.Active_player);
        }
        public void Next()
        {
            int t = 4;
            active_player = Player_to_int(m.Active_player);

            Linien = m.Linien;
            Points = m.Points;

            Züge[] z = GeneriereMöglicheZuege();
            int Maximum = 0;
            int tmp_maximum;
            Züge Max_zug = z[0];
            Züge Tmp_zug;
            foreach (Züge zz in z)
            {
                Tmp_zug = zz;
                Tmp_zug.Ausführen();
                tmp_maximum = Max(t, -100000, 100000);
                if (Maximum < tmp_maximum)
                {
                    Max_zug = Tmp_zug;
                    Maximum = tmp_maximum;
                }
                Tmp_zug.Rückgängig();
                
            }
            //Maximum-Zug Ausführen
            m.Zug_ausführen(Max_zug);
        }
        private int Bewerten()
        {
            int result = 0;
            result += count_points(active_player);
            result -= count_points(not_active_player());
            return result;
        }

        private int count_points(int Player)
        {
             return Points.Count(p => p.occupied == m.Playerliste[Player]);
        }

        private int not_active_player()
        {
            if (active_player == 0) return 1;
            else return 0;
        }
        private Züge[] GeneriereMöglicheZuege()
        {
            LinkedList<Züge> tmp = new LinkedList<Züge>();
            if (m.Player_2_set_count < 9)
            {
                foreach (PointM p in Points)
                {
                    if (p.occupied == null)
                    {
                        tmp.AddLast(new Züge(p,this));
                    }
                }
            }
            else
            {
                if (m.Count_remaining_knopefe(m.Playerliste[active_player]) > 3)
                {
                    #region loops
                    foreach (Linie3 l in Linien)
                    {
                        if (Player_to_int(l.Pt1().occupied) == active_player)
                        {
                            if (l.Pt2().occupied == null)
                            {
                                tmp.AddLast(new Züge(l.Pt1(), l.Pt2(), this));
                            }
                        }
                        if (Player_to_int(l.Pt2().occupied) == active_player)
                        {
                            if (l.Pt1().occupied == null)
                            {
                                tmp.AddLast(new Züge(l.Pt2(), l.Pt1(), this));
                            }
                        }
                        if (Player_to_int(l.Pt2().occupied) == active_player)
                        {
                            if (l.Pt3().occupied == null)
                            {
                                tmp.AddLast(new Züge(l.Pt2(), l.Pt3(), this));
                            }
                        }
                        if (Player_to_int(l.Pt3().occupied) == active_player)
                        {
                            if (l.Pt2().occupied == null)
                            {
                                tmp.AddLast(new Züge(l.Pt3(), l.Pt2(), this));
                            }
                        }
                    }
                    #endregion
                }
                else
                {
                    #region loops
                    foreach (PointM p in Points)
                    {
                        if (Player_to_int(p.occupied) == active_player)
                        {
                            foreach (PointM p1 in Points)
                            {
                                if (p1.occupied == null)
                                {
                                    tmp.AddLast(new Züge(p, p1, this));
                                }
                            }
                        }
                    }
                    #endregion
                }
            }
            #region Add Mühle züge

            List<Züge> tmp_mühle = new List<Züge>();
            foreach (Züge z in tmp)
            {
                if (z.e.Linie1.Mühle_mit_Punkt(z.e, m.Playerliste[active_player]) || 
                    z.e.Linie2.Mühle_mit_Punkt(z.e, m.Playerliste[active_player]))
                {
                    foreach (PointM p in Points)
                    {
                        if (p.occupied != null && !p.in_muehle && (Player_to_int(p.occupied) == not_active_player()))
                        {
                            tmp_mühle.Add(new Züge(z.s, z.e, p, this));
                            z.to_remove = true;
                        }
                    }
                }
            }
            tmp.ToList().RemoveAll(to_rem);
            foreach (Züge z in tmp_mühle)
            {
                tmp.AddFirst(z);
            }
            #endregion

            // @todo Add Sorting-Algorith for Optimisation
           return tmp.ToArray();
        }
        private bool to_rem(Züge z)
        {
            return z.to_remove;
        }
        private short Player_to_int(Player p)
        {
            if (p == null) return -1;
            if (p.Equals(m.Playerliste[0])) return 0;
            if (p.Equals(m.Playerliste[1])) return 1;
            return -1;
        }
        private int Max(int tiefe, int alpha, int beta)
        {
            bool running = true;
            int result=0;

            if (tiefe == 0)return Bewerten();
            Züge tmp_zug;
            int wert;

            active_player = not_active_player();

            Züge[] Mögl_züge = GeneriereMöglicheZuege();
            int i = 0;
            while (Mögl_züge.Length > i && running )
            {
                tmp_zug = Mögl_züge[i];
                tmp_zug.Ausführen();
                wert = Min(tiefe - 1, alpha, beta);
                tmp_zug.Rückgängig();
                i++;
                if (wert >= beta)
                {
                    result = beta;
                    running = false;
                }
                if (wert > alpha)
                {
                    alpha = wert;
                }
            }
            active_player = not_active_player();
            if (!running) return result;
            return alpha;
        }
        private int Min(int tiefe, int alpha, int beta)
        {
            bool running = true;
            int result = 0;

            if (tiefe == 0) return Bewerten();
            Züge tmp_zug;
            int wert;

            active_player = not_active_player();

            Züge[] Mögl_züge = GeneriereMöglicheZuege();
            int i = 0;
            while (Mögl_züge.Length > i && running)
            {
                tmp_zug = Mögl_züge[i];
                tmp_zug.Ausführen();
                wert = Max(tiefe - 1, alpha, beta);
                tmp_zug.Rückgängig();
                i++;
                if (wert <= alpha)
                {
                    result = alpha;
                    running = false;

                }
                if (wert < beta)
                    beta = wert;
            }
            active_player = not_active_player();
            if (!running) return result;
            return beta;
        }
    }
    public class Züge
    {
        public PointM s, e, r, r_bakup;
        public AI ai;
        public bool to_remove = false;
        public Züge(PointM End, AI ai)
        {
            e = End;
            this.ai = ai;
        }
        public Züge(PointM Start, PointM End, AI ai)
        {
            s = Start;
            e = End;
            this.ai = ai;
        }
        public Züge(PointM Start, PointM End, PointM Remove, AI ai)
        {
            s = Start;
            e = End;
            r = Remove;
            this.ai = ai;
        }
        public void Ausführen()
        {
            if (s != null)
            {
                e.occupied = s.occupied;
                e.set = s.set;
                s.occupied = null;
                s.set = false;
            }
            else
            {
                e.occupied = ai.m.Playerliste[ai.active_player];
                e.set = true;
            }
            if (r != null)
            {
                r_bakup = new PointM(0, 0);
                r_bakup.occupied = r.occupied;
                r_bakup.set = r.set;
                r.occupied = null;
                r.set = false;
            }
        }
        internal void Rückgängig()
        {
            if (s != null)
            {
                s.occupied = e.occupied;
                s.set = e.set;
                e.occupied = null;
                e.set = false;
            }
            else
            {
                e.occupied = null;
                e.set = false;
            }

            if (r != null)
            {
                r.occupied = r_bakup.occupied;
                r.set = r_bakup.set;
            }

        }
    }
}
